laces 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/CONTRIBUTING.md +38 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +122 -0
  4. data/LICENSE +21 -0
  5. data/README.md +5 -0
  6. data/Rakefile +8 -0
  7. data/bin/laces +16 -0
  8. data/features/creating_a_heroku_app.feature +9 -0
  9. data/features/rake_clean.feature +21 -0
  10. data/features/skipping_clearance.feature +13 -0
  11. data/features/step_definitions/gem_steps.rb +5 -0
  12. data/features/step_definitions/heroku_steps.rb +3 -0
  13. data/features/step_definitions/shell_steps.rb +55 -0
  14. data/features/support/bin/heroku +5 -0
  15. data/features/support/env.rb +15 -0
  16. data/features/support/fake_heroku.rb +21 -0
  17. data/laces-0.0.1.gem +0 -0
  18. data/laces.gemspec +35 -0
  19. data/lib/laces/actions.rb +35 -0
  20. data/lib/laces/app_builder.rb +237 -0
  21. data/lib/laces/generators/app_generator.rb +111 -0
  22. data/lib/laces/version.rb +3 -0
  23. data/templates/.DS_Store +0 -0
  24. data/templates/Gemfile_template +76 -0
  25. data/templates/HEROKU_README.md +66 -0
  26. data/templates/Procfile +1 -0
  27. data/templates/README.md +81 -0
  28. data/templates/app/assets/imgs/glyphicons-halflings-white.png +0 -0
  29. data/templates/app/assets/imgs/glyphicons-halflings.png +0 -0
  30. data/templates/app/assets/javascripts/admin.coffee +20 -0
  31. data/templates/app/assets/javascripts/application.coffee +21 -0
  32. data/templates/app/assets/javascripts/lib/actinology.coffee +47 -0
  33. data/templates/app/assets/javascripts/lib/analytics.js +11 -0
  34. data/templates/app/assets/javascripts/lib/auth_token_sync.js +17 -0
  35. data/templates/app/assets/javascripts/lib/backbone-ui.js +2455 -0
  36. data/templates/app/assets/javascripts/lib/backbone.coffee +27 -0
  37. data/templates/app/assets/javascripts/lib/backbone/collection.js +249 -0
  38. data/templates/app/assets/javascripts/lib/backbone/events.js +64 -0
  39. data/templates/app/assets/javascripts/lib/backbone/helpers.js +68 -0
  40. data/templates/app/assets/javascripts/lib/backbone/history.js +144 -0
  41. data/templates/app/assets/javascripts/lib/backbone/model.js +291 -0
  42. data/templates/app/assets/javascripts/lib/backbone/router.coffee +45 -0
  43. data/templates/app/assets/javascripts/lib/backbone/sync.coffee +38 -0
  44. data/templates/app/assets/javascripts/lib/backbone/view.js +150 -0
  45. data/templates/app/assets/javascripts/lib/backbone_extended.coffee +276 -0
  46. data/templates/app/assets/javascripts/lib/bootstrap.js +1836 -0
  47. data/templates/app/assets/javascripts/lib/inflection.js +658 -0
  48. data/templates/app/assets/javascripts/lib/jquery-ui.js +343 -0
  49. data/templates/app/assets/javascripts/lib/jquery.hotkeys.js +102 -0
  50. data/templates/app/assets/javascripts/lib/jquery.js +4 -0
  51. data/templates/app/assets/javascripts/lib/milk.js.coffee +265 -0
  52. data/templates/app/assets/javascripts/lib/raw.js +143 -0
  53. data/templates/app/assets/javascripts/lib/strftime.js +732 -0
  54. data/templates/app/assets/javascripts/lib/throttle-debounce.js +251 -0
  55. data/templates/app/assets/javascripts/lib/timeago.js +148 -0
  56. data/templates/app/assets/javascripts/lib/underscore.js +28 -0
  57. data/templates/app/assets/styles/application.sass +21 -0
  58. data/templates/app/assets/styles/layouts/default.sass +15 -0
  59. data/templates/app/assets/styles/layouts/footer.sass +0 -0
  60. data/templates/app/assets/styles/layouts/forms.sass +34 -0
  61. data/templates/app/assets/styles/layouts/header.sass +0 -0
  62. data/templates/app/assets/styles/layouts/navigation.sass +0 -0
  63. data/templates/app/assets/styles/lib/backbone-ui.css +580 -0
  64. data/templates/app/assets/styles/lib/bootstrap.sass +4248 -0
  65. data/templates/app/assets/styles/pages/home.sass +0 -0
  66. data/templates/app/assets/styles/sessions/new.sass +0 -0
  67. data/templates/app/assets/styles/users/activate.sass +0 -0
  68. data/templates/app/assets/styles/users/new.sass +0 -0
  69. data/templates/app/assets/styles/users/suspended.sass +0 -0
  70. data/templates/app/controllers/app_controller.rb +14 -0
  71. data/templates/app/controllers/pages_controller.rb +2 -0
  72. data/templates/app/controllers/sessions_controller.rb +2 -0
  73. data/templates/app/controllers/templating_controller.rb +31 -0
  74. data/templates/app/controllers/users_controller.rb +2 -0
  75. data/templates/app/helpers/app_helper.rb +94 -0
  76. data/templates/app/helpers/users_helper.rb +53 -0
  77. data/templates/app/models/user.rb +9 -0
  78. data/templates/app/views/devise/confirmations/new.haml +9 -0
  79. data/templates/app/views/devise/passwords/edit.haml +11 -0
  80. data/templates/app/views/devise/passwords/new.haml +9 -0
  81. data/templates/app/views/devise/registrations/edit.haml +13 -0
  82. data/templates/app/views/devise/registrations/new.haml +8 -0
  83. data/templates/app/views/devise/sessions/_form.haml +7 -0
  84. data/templates/app/views/devise/sessions/new.haml +5 -0
  85. data/templates/app/views/devise/unlocks/new.haml +7 -0
  86. data/templates/app/views/layouts/_ascii.haml +0 -0
  87. data/templates/app/views/layouts/_column.haml +0 -0
  88. data/templates/app/views/layouts/_content.haml +1 -0
  89. data/templates/app/views/layouts/_extra.haml +0 -0
  90. data/templates/app/views/layouts/_footer.haml +9 -0
  91. data/templates/app/views/layouts/_head.haml +13 -0
  92. data/templates/app/views/layouts/_header.haml +6 -0
  93. data/templates/app/views/layouts/application.html.haml +16 -0
  94. data/templates/app/views/pages/home.haml +1 -0
  95. data/templates/config/app.yml +29 -0
  96. data/templates/config/application.erb +28 -0
  97. data/templates/config/database.yml +32 -0
  98. data/templates/config/initializers/devise.rb +232 -0
  99. data/templates/config/initializers/rabl_init.rb +4 -0
  100. data/templates/config/initializers/setup_mail.rb +13 -0
  101. data/templates/config/initializers/wrap_parameters.rb +12 -0
  102. data/templates/db/migrate/user_migration.rb +36 -0
  103. data/templates/laces_gitignore +9 -0
  104. data/templates/lib/development_mail_interceptor.rb +7 -0
  105. data/templates/lib/templating.rb +40 -0
  106. metadata +225 -0
@@ -0,0 +1,265 @@
1
+ # Milk is a simple, fast way to get more Mustache into your CoffeeScript and
2
+ # Javascript.
3
+ #
4
+ # Mustache templates are reasonably simple -- plain text templates are
5
+ # sprinkled with "tags", which are (by default) a pair of curly braces
6
+ # surrounding some bit of content. A good resource for Mustache can be found
7
+ # [here](mustache.github.com).
8
+ TemplateCache = {}
9
+
10
+ # Tags used for working with data get their data by looking up a name in a
11
+ # context stack. This name corresponds to a key in a hash, and the stack is
12
+ # searched top to bottom for an object with given key. Dots in names are
13
+ # special: a single dot ('.') is "top of stack", and dotted names like 'a.b.c'
14
+ # do a chained lookups.
15
+ Find = (name, stack, value = null) ->
16
+ return stack[stack.length - 1] if name == '.'
17
+ [name, parts...] = name.split(/\./)
18
+ for i in [stack.length - 1...-1]
19
+ continue unless stack[i]?
20
+ continue unless typeof stack[i] == 'object' and name of (ctx = stack[i])
21
+ value = ctx[name]
22
+ break
23
+
24
+ value = Find(part, [value]) for part in parts
25
+
26
+ # If we find a function in the stack, we'll treat it as a method, and call it
27
+ # with `this` bound to the element it came from. If a method returns a
28
+ # function, we treat it as a lambda, which doesn't have a bound `this`.
29
+ if value instanceof Function
30
+ value = do (value) -> ->
31
+ val = value.apply(ctx, arguments)
32
+ return (val instanceof Function) and val.apply(null, arguments) or val
33
+
34
+ # Null values will be coerced to the empty string.
35
+ return value
36
+
37
+ # Parsed templates are expanded by simply calling each function in turn.
38
+ Expand = (obj, tmpl, args...) -> (f.call(obj, args...) for f in tmpl).join('')
39
+
40
+ # For parsing, we'll basically need a template string to parse. We do need to
41
+ # remember to take the tag delimiters into account for the cache -- different
42
+ # parse trees can exist for the same template string!
43
+ Parse = (template, delimiters = ['{{','}}'], section = null) ->
44
+ cache = (TemplateCache[delimiters.join(' ')] ||= {})
45
+ return cache[template] if template of cache
46
+
47
+ buffer = []
48
+
49
+ # We'll use a regular expression to handle tag discovery. A proper parser
50
+ # might be faster, but this is simpler, and certainly fast enough for now.
51
+ # Since the tag delimiters may change over time, we'll want to rebuild the
52
+ # regex when they change.
53
+ BuildRegex = ->
54
+ [tagOpen, tagClose] = delimiters
55
+ return ///
56
+ ([\s\S]*?) # Capture the pre-tag content
57
+ ([#{' '}\t]*) # Capture the pre-tag whitespace
58
+ (?: #{tagOpen} \s* # Match the opening tag
59
+ (?:
60
+ (!) \s* ([\s\S]+?) | # Comments
61
+ (=) \s* ([\s\S]+?) \s* = | # Set Delimiters
62
+ ({) \s* (\w[\S]*?) \s* } | # Triple Mustaches
63
+ ([^0-9a-zA-Z._!={]?) \s* ([\w.][\S]*?) # Everything else
64
+ )
65
+ \s* #{tagClose} ) # Match the closing tag
66
+ ///gm
67
+
68
+ tagPattern = BuildRegex()
69
+ tagPattern.lastIndex = pos = (section || { start: 0 }).start
70
+
71
+ # Useful errors should always be prefered - we should compile as much
72
+ # relevant information as possible.
73
+ parseError = (pos, msg) ->
74
+ (endOfLine = /$/gm).lastIndex = pos
75
+ endOfLine.exec(template)
76
+
77
+ parsedLines = template.substr(0, pos).split('\n')
78
+ lineNo = parsedLines.length
79
+ lastLine = parsedLines[lineNo - 1]
80
+ tagStart = contentEnd + whitespace.length
81
+ lastTag = template.substr(tagStart + 1, pos - tagStart - 1)
82
+
83
+ indent = new Array(lastLine.length - lastTag.length + 1).join(' ')
84
+ carets = new Array(lastTag.length + 1).join('^')
85
+ lastLine = lastLine + template.substr(pos, endOfLine.lastIndex - pos)
86
+
87
+ error = new Error()
88
+ error[key] = e[key] for key of e =
89
+ "message": "#{msg}\n\nLine #{lineNo}:\n#{lastLine}\n#{indent}#{carets}"
90
+ "error": msg, "line": lineNo, "char": indent.length, "tag": lastTag
91
+ return error
92
+
93
+ # As we start matching things, let's pull out our captures and build indices.
94
+ while match = tagPattern.exec(template)
95
+ [content, whitespace] = match[1..2]
96
+ type = match[3] || match[5] || match[7] || match[9]
97
+ tag = match[4] || match[6] || match[8] || match[10]
98
+
99
+ contentEnd = (pos + content.length) - 1
100
+ pos = tagPattern.lastIndex
101
+
102
+ # Standalone tags are tags on lines without any non-whitespace characters.
103
+ isStandalone = (contentEnd == -1 or template.charAt(contentEnd) == '\n') &&
104
+ template.charAt(pos) in [ undefined, '', '\r', '\n' ]
105
+
106
+ # We should just add static content to the buffer.
107
+ buffer.push(do (content) -> -> content) if content
108
+
109
+ # If we're dealing with a standalone tag that's not interpolation, we
110
+ # should consume the newline immediately following the tag. If we're not,
111
+ # we need to buffer the whitespace we captured earlier.
112
+ if isStandalone and type not in ['', '&', '{']
113
+ pos += 1 if template.charAt(pos) == '\r'
114
+ pos += 1 if template.charAt(pos) == '\n'
115
+ else if whitespace
116
+ buffer.push(do (whitespace) -> -> whitespace)
117
+ contentEnd += whitespace.length
118
+ whitespace = ''
119
+
120
+ # Now we'll handle the tag itself:
121
+ switch type
122
+
123
+ # Comment tags should simply be ignored.
124
+ when '!' then break
125
+
126
+ # Interpolations are handled by finding the value in the context stack,
127
+ # calling and rendering lambdas, and escaping the value if appropriate.
128
+ when '', '&', '{'
129
+ buildInterpolationTag = (name, is_unescaped) ->
130
+ return (context) ->
131
+ if (value = Find(name, context) ? '') instanceof Function
132
+ value = Expand(this, Parse("#{value()}"), arguments...)
133
+ #############################
134
+ # AB: talk about an ugly hack
135
+ #############################
136
+ if window.DontEscape is undefined || window.DontEscape is false
137
+ value = @escape("#{value}") unless is_unescaped
138
+ return "#{value}"
139
+ buffer.push(buildInterpolationTag(tag, type))
140
+
141
+ # Partial data is looked up lazily by the given function, indented as
142
+ # appropriate, and then rendered.
143
+ when '>'
144
+ buildPartialTag = (name, indentation) ->
145
+ return (context, partials) ->
146
+ partial = partials(name).toString()
147
+ partial = partial.replace(/^(?=.)/gm, indentation) if indentation
148
+ return Expand(this, Parse(partial), arguments...)
149
+ buffer.push(buildPartialTag(tag, whitespace))
150
+
151
+ # Sections and Inverted Sections make a recursive parsing pass, allowing
152
+ # us to use the call stack to handle section parsing. This will go until
153
+ # it reaches the matching End Section tag, when it will return the
154
+ # (cached!) template it parsed, along with the index it stopped at.
155
+ when '#', '^'
156
+ sectionInfo =
157
+ name: tag, start: pos
158
+ error: parseError(tagPattern.lastIndex, "Unclosed section '#{tag}'!")
159
+ [tmpl, pos] = Parse(template, delimiters, sectionInfo)
160
+
161
+ # Sections are rendered by finding the value in the context stack,
162
+ # coercing it into an array (unless the value is falsey), and rendering
163
+ # the template with each element of the array taking a turn atop the
164
+ # context stack. If the value was a function, the template is filtered
165
+ # through it before rendering.
166
+ sectionInfo['#'] = buildSectionTag = (name, delims, raw) ->
167
+ return (context) ->
168
+ value = Find(name, context) || []
169
+ tmpl = if value instanceof Function then value(raw) else raw
170
+ value = [value] unless value instanceof Array
171
+ parsed = Parse(tmpl || '', delims)
172
+
173
+ context.push(value)
174
+ result = for v in value
175
+ context[context.length - 1] = v
176
+ Expand(this, parsed, arguments...)
177
+ context.pop()
178
+
179
+ return result.join('')
180
+
181
+ # Inverted Sections render under almost opposite conditions: their
182
+ # contents will only be rendered when the retrieved value is either
183
+ # falsey or an empty array.
184
+ sectionInfo['^'] = buildInvertedSectionTag = (name, delims, raw) ->
185
+ return (context) ->
186
+ value = Find(name, context) || []
187
+ value = [1] unless value instanceof Array
188
+ value = if value.length is 0 then Parse(raw, delims) else []
189
+ return Expand(this, value, arguments...)
190
+
191
+ buffer.push(sectionInfo[type](tag, delimiters, tmpl))
192
+
193
+ # When the parser encounters an End Section tag, it runs a couple of
194
+ # quick sanity checks, then returns control back to its caller.
195
+ when '/'
196
+ unless section?
197
+ error = "End Section tag '#{tag}' found, but not in section!"
198
+ else if tag != (name = section.name)
199
+ error = "End Section tag closes '#{tag}'; expected '#{name}'!"
200
+ throw parseError(tagPattern.lastIndex, error) if error
201
+
202
+ template = template[section.start..contentEnd]
203
+ cache[template] = buffer
204
+ return [template, pos]
205
+
206
+ # The Set Delimiters tag needs to update the delimiters after some error
207
+ # checking, and rebuild the regular expression we're using to match tags.
208
+ when '='
209
+ unless (delimiters = tag.split(/\s+/)).length == 2
210
+ error = "Set Delimiters tags should have two and only two values!"
211
+ throw parseError(tagPattern.lastIndex, error) if error
212
+
213
+ escape = /[-[\]{}()*+?.,\\^$|#]/g
214
+ delimiters = (d.replace(escape, "\\$&") for d in delimiters)
215
+ tagPattern = BuildRegex()
216
+
217
+ # Any other tag type is probably a typo.
218
+ else
219
+ throw parseError(tagPattern.lastIndex, "Unknown tag type -- #{type}")
220
+
221
+ # Now that we've finished with this tag, we prepare to parse the next one!
222
+ tagPattern.lastIndex = if pos? then pos else template.length
223
+
224
+ # At this point, we've parsed all the tags. If we've still got a `section`,
225
+ # someone left a section tag open.
226
+ throw section.error if section?
227
+
228
+ # All the tags is not all the content; if there's anything left over, append
229
+ # it to the buffer. Then we'll cache the buffer and return it!
230
+ buffer.push(-> template[pos..]) unless template.length == pos
231
+ return cache[template] = buffer
232
+
233
+ # ### Public API
234
+
235
+ # The exported object (globally `Milk` in browsers) forms Milk's public API:
236
+ Milk =
237
+ VERSION: '1.2.0'
238
+ # Helpers are a form of context, implicitly on the bottom of the stack. This
239
+ # is a global value, and may be either an object or an array.
240
+ helpers: []
241
+ # Partials may also be provided globally.
242
+ partials: null
243
+ # The `escape` method performs basic content escaping, and may be either
244
+ # called or overridden with an alternate escaping mechanism.
245
+ escape: (value) ->
246
+ entities = { '&': 'amp', '"': 'quot', '<': 'lt', '>': 'gt' }
247
+ return value.replace(/[&"<>]/g, (ch) -> "&#{ entities[ch] };")
248
+ # Rendering is simple: given a template and some data, it populates the
249
+ # template. If your template uses Partial Tags, you may also supply a hash or
250
+ # a function, or simply override `Milk.partials`. There is no Step Three.
251
+ render: (template, data, partials = null) ->
252
+ unless (partials ||= @partials || {}) instanceof Function
253
+ partials = do (partials) -> (name) ->
254
+ throw "Unknown partial '#{name}'!" unless name of partials
255
+ return Find(name, [partials])
256
+
257
+ context = if @helpers instanceof Array then @helpers else [@helpers]
258
+ return Expand(this, Parse(template), context.concat([data]), partials)
259
+
260
+ # Happy hacking!
261
+ if exports?
262
+ exports[key] = Milk[key] for key of Milk
263
+ else
264
+ this.Milk = Milk
265
+
@@ -0,0 +1,143 @@
1
+ Component = {}
2
+
3
+ App = {
4
+ Views: {
5
+ Chapters : {},
6
+ ChapterItems : {},
7
+ Professors : {},
8
+ Documents : {},
9
+ Shared : {},
10
+ RewardRedemptions : {},
11
+ Downloads : {},
12
+ Transactions : {},
13
+ Uploads : {},
14
+ Rewards : {},
15
+ Videos : {},
16
+ Reviews : {},
17
+ Products : {},
18
+ Goals : {},
19
+ Pages : {},
20
+ Sessions : {},
21
+ Groups : {},
22
+ Members : {},
23
+ Courses : {},
24
+ Favorites : {},
25
+ Textbooks : {},
26
+ Decks : {},
27
+ DeckrunScores : {},
28
+ Flashcards : {},
29
+ Comments : {},
30
+ Owners : {},
31
+ Enrollments : {},
32
+ Users: {
33
+ Settings : {}
34
+ }
35
+ },
36
+ Routers: {
37
+ Professors : {},
38
+ Documents : {},
39
+ Transactions : {},
40
+ Courses : {},
41
+ Videos : {},
42
+ Downloads : {},
43
+ RewardRedemptions : {},
44
+ Pages : {},
45
+ Uploads : {},
46
+ Products : {},
47
+ Purchases : {},
48
+ Rewards : {},
49
+ Reviews : {},
50
+ Goals : {},
51
+ Sessions : {},
52
+ Users : {},
53
+ Favorites : {},
54
+ Groups : {},
55
+ Textbooks : {},
56
+ Decks : {},
57
+ Flashcards : {},
58
+ Members : {},
59
+ Shared : {},
60
+ Comments : {},
61
+ Owners : {},
62
+ Enrollments : {}
63
+ },
64
+ Models : {},
65
+ Collections : {},
66
+ Helpers : {}
67
+ };
68
+ Admin = {
69
+ Views: {
70
+ Transactions : {},
71
+ FacebookShares : {},
72
+ Promos : {},
73
+ Documents : {},
74
+ Goals : {},
75
+ Campuses : {},
76
+ Professors : {},
77
+ Departments : {},
78
+ Rewards : {},
79
+ RewardRedemptions : {},
80
+ PromoRedemptions : {},
81
+ Products : {},
82
+ Purchases : {},
83
+ Videos : {},
84
+ Schools : {},
85
+ Searches : {},
86
+ Credits : {},
87
+ Courses : {},
88
+ Stats : {},
89
+ Users : {},
90
+ Shared : {},
91
+ Reviews : {},
92
+ Downloads : {},
93
+ Enrollments : {},
94
+ Referrals : {},
95
+ Logins : {},
96
+ Banners : {},
97
+ Groups : {},
98
+ Members : {},
99
+ Reports : {},
100
+ Ambassadors : {},
101
+ EmailTemplates : {}
102
+ },
103
+ Routers: {
104
+ Logins : {},
105
+ Transactions : {},
106
+ FacebookShares : {},
107
+ Promos : {},
108
+ Documents : {},
109
+ Campuses : {},
110
+ Professors : {},
111
+ Departments : {},
112
+ RewardRedemptions : {},
113
+ Goals : {},
114
+ PromoRedemptions : {},
115
+ Products : {},
116
+ Purchases : {},
117
+ Rewards : {},
118
+ Courses : {},
119
+ Schools : {},
120
+ Searches : {},
121
+ Credits : {},
122
+ Videos : {},
123
+ Users : {},
124
+ Stats : {},
125
+ Reviews : {},
126
+ Downloads : {},
127
+ Enrollments : {},
128
+ Referrals : {},
129
+ Banners : {},
130
+ Groups : {},
131
+ Members : {},
132
+ Reports : {},
133
+ Ambassadors : {},
134
+ EmailTemplates : {}
135
+ },
136
+ Models : {},
137
+ Collections : {},
138
+ Helpers : {}
139
+ };
140
+ window.App = App;
141
+ window.Admin = Admin;
142
+ window._Router = null;
143
+ window.App.live = {}
@@ -0,0 +1,732 @@
1
+ /*
2
+ strftime for Javascript
3
+ Copyright (c) 2008, Philip S Tellis <philip@bluesmoon.info>
4
+ All rights reserved.
5
+
6
+ This code is distributed under the terms of the BSD licence
7
+
8
+ Redistribution and use of this software in source and binary forms, with or without modification,
9
+ are permitted provided that the following conditions are met:
10
+
11
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
12
+ and the following disclaimer.
13
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
14
+ conditions and the following disclaimer in the documentation and/or other materials provided
15
+ with the distribution.
16
+ * The names of the contributors to this file may not be used to endorse or promote products
17
+ derived from this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
20
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
21
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
29
+ /**
30
+ * \file strftime.js
31
+ * \author Philip S Tellis \<philip@bluesmoon.info\>
32
+ * \version 1.3
33
+ * \date 2008/06
34
+ * \brief Javascript implementation of strftime
35
+ *
36
+ * Implements strftime for the Date object in javascript based on the PHP implementation described at
37
+ * http://www.php.net/strftime This is in turn based on the Open Group specification defined
38
+ * at http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html This implementation does not
39
+ * include modified conversion specifiers (i.e., Ex and Ox)
40
+ *
41
+ * The following format specifiers are supported:
42
+ *
43
+ * \copydoc formats
44
+ *
45
+ * \%a, \%A, \%b and \%B should be localised for non-English locales.
46
+ *
47
+ * \par Usage:
48
+ * This library may be used as follows:
49
+ * \code
50
+ * var d = new Date();
51
+ *
52
+ * var ymd = d.strftime('%Y/%m/%d');
53
+ * var iso = d.strftime('%Y-%m-%dT%H:%M:%S%z');
54
+ *
55
+ * \endcode
56
+ *
57
+ * \sa \link Date.prototype.strftime Date.strftime \endlink for a description of each of the supported format specifiers
58
+ * \sa Date.ext.locales for localisation information
59
+ * \sa http://www.php.net/strftime for the PHP implementation which is the basis for this
60
+ * \sa http://tech.bluesmoon.info/2008/04/strftime-in-javascript.html for feedback
61
+ */
62
+
63
+ //! Date extension object - all supporting objects go in here.
64
+ Date.ext = {};
65
+
66
+ //! Utility methods
67
+ Date.ext.util = {};
68
+
69
+ /**
70
+ \brief Left pad a number with something
71
+ \details Takes a number and pads it to the left with the passed in pad character
72
+ \param x The number to pad
73
+ \param pad The string to pad with
74
+ \param r [optional] Upper limit for pad. A value of 10 pads to 2 digits, a value of 100 pads to 3 digits.
75
+ Default is 10.
76
+
77
+ \return The number left padded with the pad character. This function returns a string and not a number.
78
+ */
79
+ Date.ext.util.xPad=function(x, pad, r)
80
+ {
81
+ if(typeof(r) == 'undefined')
82
+ {
83
+ r=10;
84
+ }
85
+ for( ; parseInt(x, 10)<r && r>1; r/=10)
86
+ x = pad.toString() + x;
87
+ return x.toString();
88
+ };
89
+
90
+ /**
91
+ \brief Currently selected locale.
92
+ \details
93
+ The locale for a specific date object may be changed using \code Date.locale = "new-locale"; \endcode
94
+ The default will be based on the lang attribute of the HTML tag of your document
95
+ */
96
+ Date.prototype.locale = 'en-GB';
97
+ //! \cond FALSE
98
+ if(document.getElementsByTagName('html') && document.getElementsByTagName('html')[0].lang)
99
+ {
100
+ Date.prototype.locale = document.getElementsByTagName('html')[0].lang;
101
+ }
102
+ //! \endcond
103
+
104
+ /**
105
+ \brief Localised strings for days of the week and months of the year.
106
+ \details
107
+ To create your own local strings, add a locale object to the locales object.
108
+ The key of your object should be the same as your locale name. For example:
109
+ en-US,
110
+ fr,
111
+ fr-CH,
112
+ de-DE
113
+ Names are case sensitive and are described at http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
114
+ Your locale object must contain the following keys:
115
+ \param a Short names of days of week starting with Sunday
116
+ \param A Long names days of week starting with Sunday
117
+ \param b Short names of months of the year starting with January
118
+ \param B Long names of months of the year starting with February
119
+ \param c The preferred date and time representation in your locale
120
+ \param p AM or PM in your locale
121
+ \param P am or pm in your locale
122
+ \param x The preferred date representation for the current locale without the time.
123
+ \param X The preferred time representation for the current locale without the date.
124
+
125
+ \sa Date.ext.locales.en for a sample implementation
126
+ \sa \ref localisation for detailed documentation on localising strftime for your own locale
127
+ */
128
+ Date.ext.locales = { };
129
+
130
+ /**
131
+ * \brief Localised strings for English (British).
132
+ * \details
133
+ * This will be used for any of the English dialects unless overridden by a country specific one.
134
+ * This is the default locale if none specified
135
+ */
136
+ Date.ext.locales.en = {
137
+ a: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
138
+ A: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
139
+ b: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
140
+ B: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
141
+ c: '%a %d %b %Y %T %Z',
142
+ p: ['AM', 'PM'],
143
+ P: ['am', 'pm'],
144
+ x: '%d/%m/%y',
145
+ X: '%T'
146
+ };
147
+
148
+ //! \cond FALSE
149
+ // Localised strings for US English
150
+ Date.ext.locales['en-US'] = Date.ext.locales.en;
151
+ Date.ext.locales['en-US'].c = '%a %d %b %Y %r %Z';
152
+ Date.ext.locales['en-US'].x = '%D';
153
+ Date.ext.locales['en-US'].X = '%r';
154
+
155
+ // Localised strings for British English
156
+ Date.ext.locales['en-GB'] = Date.ext.locales.en;
157
+
158
+ // Localised strings for Australian English
159
+ Date.ext.locales['en-AU'] = Date.ext.locales['en-GB'];
160
+ //! \endcond
161
+
162
+ //! \brief List of supported format specifiers.
163
+ /**
164
+ * \details
165
+ * \arg \%a - abbreviated weekday name according to the current locale
166
+ * \arg \%A - full weekday name according to the current locale
167
+ * \arg \%b - abbreviated month name according to the current locale
168
+ * \arg \%B - full month name according to the current locale
169
+ * \arg \%c - preferred date and time representation for the current locale
170
+ * \arg \%C - century number (the year divided by 100 and truncated to an integer, range 00 to 99)
171
+ * \arg \%d - day of the month as a decimal number (range 01 to 31)
172
+ * \arg \%D - same as %m/%d/%y
173
+ * \arg \%e - day of the month as a decimal number, a single digit is preceded by a space (range ' 1' to '31')
174
+ * \arg \%g - like %G, but without the century
175
+ * \arg \%G - The 4-digit year corresponding to the ISO week number
176
+ * \arg \%h - same as %b
177
+ * \arg \%H - hour as a decimal number using a 24-hour clock (range 00 to 23)
178
+ * \arg \%I - hour as a decimal number using a 12-hour clock (range 01 to 12)
179
+ * \arg \%j - day of the year as a decimal number (range 001 to 366)
180
+ * \arg \%m - month as a decimal number (range 01 to 12)
181
+ * \arg \%M - minute as a decimal number
182
+ * \arg \%n - newline character
183
+ * \arg \%p - either `AM' or `PM' according to the given time value, or the corresponding strings for the current locale
184
+ * \arg \%P - like %p, but lower case
185
+ * \arg \%r - time in a.m. and p.m. notation equal to %I:%M:%S %p
186
+ * \arg \%R - time in 24 hour notation equal to %H:%M
187
+ * \arg \%S - second as a decimal number
188
+ * \arg \%t - tab character
189
+ * \arg \%T - current time, equal to %H:%M:%S
190
+ * \arg \%u - weekday as a decimal number [1,7], with 1 representing Monday
191
+ * \arg \%U - week number of the current year as a decimal number, starting with
192
+ * the first Sunday as the first day of the first week
193
+ * \arg \%V - The ISO 8601:1988 week number of the current year as a decimal number,
194
+ * range 01 to 53, where week 1 is the first week that has at least 4 days
195
+ * in the current year, and with Monday as the first day of the week.
196
+ * \arg \%w - day of the week as a decimal, Sunday being 0
197
+ * \arg \%W - week number of the current year as a decimal number, starting with the
198
+ * first Monday as the first day of the first week
199
+ * \arg \%x - preferred date representation for the current locale without the time
200
+ * \arg \%X - preferred time representation for the current locale without the date
201
+ * \arg \%y - year as a decimal number without a century (range 00 to 99)
202
+ * \arg \%Y - year as a decimal number including the century
203
+ * \arg \%z - numerical time zone representation
204
+ * \arg \%Z - time zone name or abbreviation
205
+ * \arg \%% - a literal `\%' character
206
+ */
207
+ Date.ext.formats = {
208
+ a: function(d) { return Date.ext.locales[d.locale].a[d.getDay()]; },
209
+ A: function(d) { return Date.ext.locales[d.locale].A[d.getDay()]; },
210
+ b: function(d) { return Date.ext.locales[d.locale].b[d.getMonth()]; },
211
+ B: function(d) { return Date.ext.locales[d.locale].B[d.getMonth()]; },
212
+ c: 'toLocaleString',
213
+ C: function(d) { return Date.ext.util.xPad(parseInt(d.getFullYear()/100, 10), 0); },
214
+ d: ['getDate', '0'],
215
+ e: ['getDate', ' '],
216
+ g: function(d) { return Date.ext.util.xPad(parseInt(Date.ext.util.G(d)/100, 10), 0); },
217
+ G: function(d) {
218
+ var y = d.getFullYear();
219
+ var V = parseInt(Date.ext.formats.V(d), 10);
220
+ var W = parseInt(Date.ext.formats.W(d), 10);
221
+
222
+ if(W > V) {
223
+ y++;
224
+ } else if(W===0 && V>=52) {
225
+ y--;
226
+ }
227
+
228
+ return y;
229
+ },
230
+ H: ['getHours', '0'],
231
+ I: function(d) { var I=d.getHours()%12; return Date.ext.util.xPad(I===0?12:I, 0); },
232
+ j: function(d) {
233
+ var ms = d - new Date('' + d.getFullYear() + '/1/1 GMT');
234
+ ms += d.getTimezoneOffset()*60000;
235
+ var doy = parseInt(ms/60000/60/24, 10)+1;
236
+ return Date.ext.util.xPad(doy, 0, 100);
237
+ },
238
+ m: function(d) { return Date.ext.util.xPad(d.getMonth()+1, 0); },
239
+ M: ['getMinutes', '0'],
240
+ p: function(d) { return Date.ext.locales[d.locale].p[d.getHours() >= 12 ? 1 : 0 ]; },
241
+ P: function(d) { return Date.ext.locales[d.locale].P[d.getHours() >= 12 ? 1 : 0 ]; },
242
+ S: ['getSeconds', '0'],
243
+ u: function(d) { var dow = d.getDay(); return dow===0?7:dow; },
244
+ U: function(d) {
245
+ var doy = parseInt(Date.ext.formats.j(d), 10);
246
+ var rdow = 6-d.getDay();
247
+ var woy = parseInt((doy+rdow)/7, 10);
248
+ return Date.ext.util.xPad(woy, 0);
249
+ },
250
+ V: function(d) {
251
+ var woy = parseInt(Date.ext.formats.W(d), 10);
252
+ var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay();
253
+ // First week is 01 and not 00 as in the case of %U and %W,
254
+ // so we add 1 to the final result except if day 1 of the year
255
+ // is a Monday (then %W returns 01).
256
+ // We also need to subtract 1 if the day 1 of the year is
257
+ // Friday-Sunday, so the resulting equation becomes:
258
+ var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1);
259
+ if(idow == 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4)
260
+ {
261
+ idow = 1;
262
+ }
263
+ else if(idow === 0)
264
+ {
265
+ idow = Date.ext.formats.V(new Date('' + (d.getFullYear()-1) + '/12/31'));
266
+ }
267
+
268
+ return Date.ext.util.xPad(idow, 0);
269
+ },
270
+ w: 'getDay',
271
+ W: function(d) {
272
+ var doy = parseInt(Date.ext.formats.j(d), 10);
273
+ var rdow = 7-Date.ext.formats.u(d);
274
+ var woy = parseInt((doy+rdow)/7, 10);
275
+ return Date.ext.util.xPad(woy, 0, 10);
276
+ },
277
+ y: function(d) { return Date.ext.util.xPad(d.getFullYear()%100, 0); },
278
+ Y: 'getFullYear',
279
+ z: function(d) {
280
+ var o = d.getTimezoneOffset();
281
+ var H = Date.ext.util.xPad(parseInt(Math.abs(o/60), 10), 0);
282
+ var M = Date.ext.util.xPad(o%60, 0);
283
+ return (o>0?'-':'+') + H + M;
284
+ },
285
+ Z: function(d) { return d.toString().replace(/^.*\(([^)]+)\)$/, '$1'); },
286
+ '%': function(d) { return '%'; }
287
+ };
288
+
289
+ /**
290
+ \brief List of aggregate format specifiers.
291
+ \details
292
+ Aggregate format specifiers map to a combination of basic format specifiers.
293
+ These are implemented in terms of Date.ext.formats.
294
+
295
+ A format specifier that maps to 'locale' is read from Date.ext.locales[current-locale].
296
+
297
+ \sa Date.ext.formats
298
+ */
299
+ Date.ext.aggregates = {
300
+ c: 'locale',
301
+ D: '%m/%d/%y',
302
+ h: '%b',
303
+ n: '\n',
304
+ r: '%I:%M:%S %p',
305
+ R: '%H:%M',
306
+ t: '\t',
307
+ T: '%H:%M:%S',
308
+ x: 'locale',
309
+ X: 'locale'
310
+ };
311
+
312
+ //! \cond FALSE
313
+ // Cache timezone values because they will never change for a given JS instance
314
+ Date.ext.aggregates.z = Date.ext.formats.z(new Date());
315
+ Date.ext.aggregates.Z = Date.ext.formats.Z(new Date());
316
+ //! \endcond
317
+
318
+ //! List of unsupported format specifiers.
319
+ /**
320
+ * \details
321
+ * All format specifiers supported by the PHP implementation are supported by
322
+ * this javascript implementation.
323
+ */
324
+ Date.ext.unsupported = { };
325
+
326
+
327
+ /**
328
+ * \brief Formats the date according to the specified format.
329
+ * \param fmt The format to format the date in. This may be a combination of the following:
330
+ * \copydoc formats
331
+ *
332
+ * \return A string representation of the date formatted based on the passed in parameter
333
+ * \sa http://www.php.net/strftime for documentation on format specifiers
334
+ */
335
+ Date.prototype.strftime=function(fmt)
336
+ {
337
+ // Fix locale if declared locale hasn't been defined
338
+ // After the first call this condition should never be entered unless someone changes the locale
339
+ if(!(this.locale in Date.ext.locales))
340
+ {
341
+ if(this.locale.replace(/-[a-zA-Z]+$/, '') in Date.ext.locales)
342
+ {
343
+ this.locale = this.locale.replace(/-[a-zA-Z]+$/, '');
344
+ }
345
+ else
346
+ {
347
+ this.locale = 'en-GB';
348
+ }
349
+ }
350
+
351
+ var d = this;
352
+ // First replace aggregates
353
+ while(fmt.match(/%[cDhnrRtTxXzZ]/))
354
+ {
355
+ fmt = fmt.replace(/%([cDhnrRtTxXzZ])/g, function(m0, m1)
356
+ {
357
+ var f = Date.ext.aggregates[m1];
358
+ return (f == 'locale' ? Date.ext.locales[d.locale][m1] : f);
359
+ });
360
+ }
361
+
362
+
363
+ // Now replace formats - we need a closure so that the date object gets passed through
364
+ var str = fmt.replace(/%([aAbBCdegGHIjmMpPSuUVwWyY%])/g, function(m0, m1)
365
+ {
366
+ var f = Date.ext.formats[m1];
367
+ if(typeof(f) == 'string') {
368
+ return d[f]();
369
+ } else if(typeof(f) == 'function') {
370
+ return f.call(d, d);
371
+ } else if(typeof(f) == 'object' && typeof(f[0]) == 'string') {
372
+ return Date.ext.util.xPad(d[f[0]](), f[1]);
373
+ } else {
374
+ return m1;
375
+ }
376
+ });
377
+ d=null;
378
+ return str;
379
+ };
380
+
381
+ /**
382
+ * \mainpage strftime for Javascript
383
+ *
384
+ * \section toc Table of Contents
385
+ * - \ref intro_sec
386
+ * - <a class="el" href="strftime.js">Download full source</a> / <a class="el" href="strftime-min.js">minified</a>
387
+ * - \subpage usage
388
+ * - \subpage format_specifiers
389
+ * - \subpage localisation
390
+ * - \link strftime.js API Documentation \endlink
391
+ * - \subpage demo
392
+ * - \subpage changelog
393
+ * - \subpage faq
394
+ * - <a class="el" href="http://tech.bluesmoon.info/2008/04/strftime-in-javascript.html">Feedback</a>
395
+ * - \subpage copyright_licence
396
+ *
397
+ * \section intro_sec Introduction
398
+ *
399
+ * C and PHP developers have had access to a built in strftime function for a long time.
400
+ * This function is an easy way to format dates and times for various display needs.
401
+ *
402
+ * This library brings the flexibility of strftime to the javascript Date object
403
+ *
404
+ * Use this library if you frequently need to format dates in javascript in a variety of ways. For example,
405
+ * if you have PHP code that writes out formatted dates, and want to mimic the functionality using
406
+ * progressively enhanced javascript, then this library can do exactly what you want.
407
+ *
408
+ *
409
+ *
410
+ *
411
+ * \page usage Example usage
412
+ *
413
+ * \section usage_sec Usage
414
+ * This library may be used as follows:
415
+ * \code
416
+ * var d = new Date();
417
+ *
418
+ * var ymd = d.strftime('%Y/%m/%d');
419
+ * var iso = d.strftime('%Y-%m-%dT%H:%M:%S%z');
420
+ *
421
+ * \endcode
422
+ *
423
+ * \subsection examples Examples
424
+ *
425
+ * To get the current time in hours and minutes:
426
+ * \code
427
+ * var d = new Date();
428
+ * d.strftime("%H:%M");
429
+ * \endcode
430
+ *
431
+ * To get the current time with seconds in AM/PM notation:
432
+ * \code
433
+ * var d = new Date();
434
+ * d.strftime("%r");
435
+ * \endcode
436
+ *
437
+ * To get the year and day of the year for August 23, 2009:
438
+ * \code
439
+ * var d = new Date('2009/8/23');
440
+ * d.strftime("%Y-%j");
441
+ * \endcode
442
+ *
443
+ * \section demo_sec Demo
444
+ *
445
+ * Try your own examples on the \subpage demo page. You can use any of the supported
446
+ * \subpage format_specifiers.
447
+ *
448
+ *
449
+ *
450
+ *
451
+ * \page localisation Localisation
452
+ * You can localise strftime by implementing the short and long forms for days of the
453
+ * week and months of the year, and the localised aggregates for the preferred date
454
+ * and time representation for your locale. You need to add your locale to the
455
+ * Date.ext.locales object.
456
+ *
457
+ * \section localising_fr Localising for french
458
+ *
459
+ * For example, this is how we'd add French language strings to the locales object:
460
+ * \dontinclude index.html
461
+ * \skip Generic french
462
+ * \until };
463
+ * The % format specifiers are all defined in \ref formats. You can use any of those.
464
+ *
465
+ * This locale definition may be included in your own source file, or in the HTML file
466
+ * including \c strftime.js, however it must be defined \em after including \c strftime.js
467
+ *
468
+ * The above definition includes generic french strings and formats that are used in France.
469
+ * Other french speaking countries may have other representations for dates and times, so we
470
+ * need to override this for them. For example, Canadian french uses a Y-m-d date format,
471
+ * while French french uses d.m.Y. We fix this by defining Canadian french to be the same
472
+ * as generic french, and then override the format specifiers for \c x for the \c fr-CA locale:
473
+ * \until End french
474
+ *
475
+ * You can now use any of the French locales at any time by setting \link Date.prototype.locale Date.locale \endlink
476
+ * to \c "fr", \c "fr-FR", \c "fr-CA", or any other french dialect:
477
+ * \code
478
+ * var d = new Date("2008/04/22");
479
+ * d.locale = "fr";
480
+ *
481
+ * d.strftime("%A, %d %B == %x");
482
+ * \endcode
483
+ * will return:
484
+ * \code
485
+ * mardi, 22 avril == 22.04.2008
486
+ * \endcode
487
+ * While changing the locale to "fr-CA":
488
+ * \code
489
+ * d.locale = "fr-CA";
490
+ *
491
+ * d.strftime("%A, %d %B == %x");
492
+ * \endcode
493
+ * will return:
494
+ * \code
495
+ * mardi, 22 avril == 2008-04-22
496
+ * \endcode
497
+ *
498
+ * You can use any of the format specifiers defined at \ref formats
499
+ *
500
+ * The locale for all dates defaults to the value of the \c lang attribute of your HTML document if
501
+ * it is set, or to \c "en" otherwise.
502
+ * \note
503
+ * Your locale definitions \b MUST be added to the locale object before calling
504
+ * \link Date.prototype.strftime Date.strftime \endlink.
505
+ *
506
+ * \sa \ref formats for a list of format specifiers that can be used in your definitions
507
+ * for c, x and X.
508
+ *
509
+ * \section locale_names Locale names
510
+ *
511
+ * Locale names are defined in RFC 1766. Typically, a locale would be a two letter ISO639
512
+ * defined language code and an optional ISO3166 defined country code separated by a -
513
+ *
514
+ * eg: fr-FR, de-DE, hi-IN
515
+ *
516
+ * \sa http://www.ietf.org/rfc/rfc1766.txt
517
+ * \sa http://www.loc.gov/standards/iso639-2/php/code_list.php
518
+ * \sa http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
519
+ *
520
+ * \section locale_fallback Locale fallbacks
521
+ *
522
+ * If a locale object corresponding to the fully specified locale isn't found, an attempt will be made
523
+ * to fall back to the two letter language code. If a locale object corresponding to that isn't found
524
+ * either, then the locale will fall back to \c "en". No warning will be issued.
525
+ *
526
+ * For example, if we define a locale for de:
527
+ * \until };
528
+ * Then set the locale to \c "de-DE":
529
+ * \code
530
+ * d.locale = "de-DE";
531
+ *
532
+ * d.strftime("%a, %d %b");
533
+ * \endcode
534
+ * In this case, the \c "de" locale will be used since \c "de-DE" has not been defined:
535
+ * \code
536
+ * Di, 22 Apr
537
+ * \endcode
538
+ *
539
+ * Swiss german will return the same since it will also fall back to \c "de":
540
+ * \code
541
+ * d.locale = "de-CH";
542
+ *
543
+ * d.strftime("%a, %d %b");
544
+ * \endcode
545
+ * \code
546
+ * Di, 22 Apr
547
+ * \endcode
548
+ *
549
+ * We need to override the \c a specifier for Swiss german, since it's different from German german:
550
+ * \until End german
551
+ * We now get the correct results:
552
+ * \code
553
+ * d.locale = "de-CH";
554
+ *
555
+ * d.strftime("%a, %d %b");
556
+ * \endcode
557
+ * \code
558
+ * Die, 22 Apr
559
+ * \endcode
560
+ *
561
+ * \section builtin_locales Built in locales
562
+ *
563
+ * This library comes with pre-defined locales for en, en-GB, en-US and en-AU.
564
+ *
565
+ *
566
+ *
567
+ *
568
+ * \page format_specifiers Format specifiers
569
+ *
570
+ * \section specifiers Format specifiers
571
+ * strftime has several format specifiers defined by the Open group at
572
+ * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html
573
+ *
574
+ * PHP added a few of its own, defined at http://www.php.net/strftime
575
+ *
576
+ * This javascript implementation supports all the PHP specifiers
577
+ *
578
+ * \subsection supp Supported format specifiers:
579
+ * \copydoc formats
580
+ *
581
+ * \subsection unsupportedformats Unsupported format specifiers:
582
+ * \copydoc unsupported
583
+ *
584
+ *
585
+ *
586
+ *
587
+ * \page demo strftime demo
588
+ * <div style="float:right;width:45%;">
589
+ * \copydoc formats
590
+ * </div>
591
+ * \htmlinclude index.html
592
+ *
593
+ *
594
+ *
595
+ *
596
+ * \page faq FAQ
597
+ *
598
+ * \section how_tos Usage
599
+ *
600
+ * \subsection howtouse Is there a manual on how to use this library?
601
+ *
602
+ * Yes, see \ref usage
603
+ *
604
+ * \subsection wheretoget Where can I get a minified version of this library?
605
+ *
606
+ * The minified version is available <a href="strftime-min.js" title="Minified strftime.js">here</a>.
607
+ *
608
+ * \subsection which_specifiers Which format specifiers are supported?
609
+ *
610
+ * See \ref format_specifiers
611
+ *
612
+ * \section whys Why?
613
+ *
614
+ * \subsection why_lib Why this library?
615
+ *
616
+ * I've used the strftime function in C, PHP and the Unix shell, and found it very useful
617
+ * to do date formatting. When I needed to do date formatting in javascript, I decided
618
+ * that it made the most sense to just reuse what I'm already familiar with.
619
+ *
620
+ * \subsection why_another Why another strftime implementation for Javascript?
621
+ *
622
+ * Yes, there are other strftime implementations for Javascript, but I saw problems with
623
+ * all of them that meant I couldn't use them directly. Some implementations had bad
624
+ * designs. For example, iterating through all possible specifiers and scanning the string
625
+ * for them. Others were tied to specific libraries like prototype.
626
+ *
627
+ * Trying to extend any of the existing implementations would have required only slightly
628
+ * less effort than writing this from scratch. In the end it took me just about 3 hours
629
+ * to write the code and about 6 hours battling with doxygen to write these docs.
630
+ *
631
+ * I also had an idea of how I wanted to implement this, so decided to try it.
632
+ *
633
+ * \subsection why_extend_date Why extend the Date class rather than subclass it?
634
+ *
635
+ * I tried subclassing Date and failed. I didn't want to waste time on figuring
636
+ * out if there was a problem in my code or if it just wasn't possible. Adding to the
637
+ * Date.prototype worked well, so I stuck with it.
638
+ *
639
+ * I did have some worries because of the way for..in loops got messed up after json.js added
640
+ * to the Object.prototype, but that isn't an issue here since {} is not a subclass of Date.
641
+ *
642
+ * My last doubt was about the Date.ext namespace that I created. I still don't like this,
643
+ * but I felt that \c ext at least makes clear that this is external or an extension.
644
+ *
645
+ * It's quite possible that some future version of javascript will add an \c ext or a \c locale
646
+ * or a \c strftime property/method to the Date class, but this library should probably
647
+ * check for capabilities before doing what it does.
648
+ *
649
+ * \section curiosity Curiosity
650
+ *
651
+ * \subsection how_big How big is the code?
652
+ *
653
+ * \arg 26K bytes with documentation
654
+ * \arg 4242 bytes minified using <a href="http://developer.yahoo.com/yui/compressor/">YUI Compressor</a>
655
+ * \arg 1477 bytes minified and gzipped
656
+ *
657
+ * \subsection how_long How long did it take to write this?
658
+ *
659
+ * 15 minutes for the idea while I was composing this blog post:
660
+ * http://tech.bluesmoon.info/2008/04/javascript-date-functions.html
661
+ *
662
+ * 3 hours in one evening to write v1.0 of the code and 6 hours the same
663
+ * night to write the docs and this manual. As you can tell, I'm fairly
664
+ * sleepy.
665
+ *
666
+ * Versions 1.1 and 1.2 were done in a couple of hours each, and version 1.3
667
+ * in under one hour.
668
+ *
669
+ * \section contributing Contributing
670
+ *
671
+ * \subsection how_to_rfe How can I request features or make suggestions?
672
+ *
673
+ * You can leave a comment on my blog post about this library here:
674
+ * http://tech.bluesmoon.info/2008/04/strftime-in-javascript.html
675
+ *
676
+ * \subsection how_to_contribute Can I/How can I contribute code to this library?
677
+ *
678
+ * Yes, that would be very nice, thank you. You can do various things. You can make changes
679
+ * to the library, and make a diff against the current file and mail me that diff at
680
+ * philip@bluesmoon.info, or you could just host the new file on your own servers and add
681
+ * your name to the copyright list at the top stating which parts you've added.
682
+ *
683
+ * If you do mail me a diff, let me know how you'd like to be listed in the copyright section.
684
+ *
685
+ * \subsection copyright_signover Who owns the copyright on contributed code?
686
+ *
687
+ * The contributor retains copyright on contributed code.
688
+ *
689
+ * In some cases I may use contributed code as a template and write the code myself. In this
690
+ * case I'll give the contributor credit for the idea, but will not add their name to the
691
+ * copyright holders list.
692
+ *
693
+ *
694
+ *
695
+ *
696
+ * \page copyright_licence Copyright & Licence
697
+ *
698
+ * \section copyright Copyright
699
+ * \dontinclude strftime.js
700
+ * \skip Copyright
701
+ * \until rights
702
+ *
703
+ * \section licence Licence
704
+ * \skip This code
705
+ * \until SUCH DAMAGE.
706
+ *
707
+ *
708
+ *
709
+ * \page changelog ChangeLog
710
+ *
711
+ * \par 1.3 - 2008/06/17:
712
+ * - Fixed padding issue with negative timezone offsets in %r
713
+ * reported and fixed by Mikko <mikko.heimola@iki.fi>
714
+ * - Added support for %P
715
+ * - Internationalised %r, %p and %P
716
+ *
717
+ * \par 1.2 - 2008/04/27:
718
+ * - Fixed support for c (previously it just returned toLocaleString())
719
+ * - Add support for c, x and X
720
+ * - Add locales for en-GB, en-US and en-AU
721
+ * - Make en-GB the default locale (previous was en)
722
+ * - Added more localisation docs
723
+ *
724
+ * \par 1.1 - 2008/04/27:
725
+ * - Fix bug in xPad which wasn't padding more than a single digit
726
+ * - Fix bug in j which had an off by one error for days after March 10th because of daylight savings
727
+ * - Add support for g, G, U, V and W
728
+ *
729
+ * \par 1.0 - 2008/04/22:
730
+ * - Initial release with support for a, A, b, B, c, C, d, D, e, H, I, j, m, M, p, r, R, S, t, T, u, w, y, Y, z, Z, and %
731
+ */
732
+