spina-openinghours 0.0.5

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 (55) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +34 -0
  5. data/app/assets/javascripts/spina/openinghours/jquery.formatter.js +814 -0
  6. data/app/assets/javascripts/spina/openinghours/openinghours.js.coffee +8 -0
  7. data/app/controllers/spina/admin/openinghours_controller.rb +43 -0
  8. data/app/decorators/controllers/spina/pages_controller_decorator.rb +12 -0
  9. data/app/models/spina/openinghour.rb +21 -0
  10. data/app/views/spina/admin/openinghours/_form.html.haml +20 -0
  11. data/app/views/spina/admin/openinghours/edit.html.haml +1 -0
  12. data/app/views/spina/admin/openinghours/index.html.haml +15 -0
  13. data/config/routes.rb +5 -0
  14. data/db/migrate/20140402111207_create_openinghours.rb +10 -0
  15. data/lib/openinghours.rb +4 -0
  16. data/lib/spina/openinghours.rb +6 -0
  17. data/lib/spina/openinghours/configuration.rb +14 -0
  18. data/lib/spina/openinghours/engine.rb +21 -0
  19. data/lib/spina/openinghours/version.rb +5 -0
  20. data/lib/tasks/openinghours_tasks.rake +4 -0
  21. data/test/dummy/README.rdoc +28 -0
  22. data/test/dummy/Rakefile +6 -0
  23. data/test/dummy/app/assets/javascripts/application.js +13 -0
  24. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  25. data/test/dummy/app/controllers/application_controller.rb +5 -0
  26. data/test/dummy/app/helpers/application_helper.rb +2 -0
  27. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  28. data/test/dummy/bin/bundle +3 -0
  29. data/test/dummy/bin/rails +4 -0
  30. data/test/dummy/bin/rake +4 -0
  31. data/test/dummy/config.ru +4 -0
  32. data/test/dummy/config/application.rb +23 -0
  33. data/test/dummy/config/boot.rb +5 -0
  34. data/test/dummy/config/database.yml +25 -0
  35. data/test/dummy/config/environment.rb +5 -0
  36. data/test/dummy/config/environments/development.rb +29 -0
  37. data/test/dummy/config/environments/production.rb +80 -0
  38. data/test/dummy/config/environments/test.rb +36 -0
  39. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  40. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  41. data/test/dummy/config/initializers/inflections.rb +16 -0
  42. data/test/dummy/config/initializers/mime_types.rb +5 -0
  43. data/test/dummy/config/initializers/secret_token.rb +12 -0
  44. data/test/dummy/config/initializers/session_store.rb +3 -0
  45. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  46. data/test/dummy/config/locales/en.yml +23 -0
  47. data/test/dummy/config/routes.rb +56 -0
  48. data/test/dummy/public/404.html +58 -0
  49. data/test/dummy/public/422.html +58 -0
  50. data/test/dummy/public/500.html +57 -0
  51. data/test/dummy/public/favicon.ico +0 -0
  52. data/test/integration/navigation_test.rb +10 -0
  53. data/test/openinghours_test.rb +7 -0
  54. data/test/test_helper.rb +15 -0
  55. metadata +158 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 03858ac5c62b6492a3223688ddf3a60e37dfc348
4
+ data.tar.gz: 67bb028eac90d3ce98ff0404d308a01bb44f5d1a
5
+ SHA512:
6
+ metadata.gz: 2ed441bca59e6e6009a11a80d0623b46f2328970076f00f0590767b3f5d656736c37b036c0e734a12146cbbc4851ca3b6896be2915ab4cfe2cc6b4d92a9e25f9
7
+ data.tar.gz: 629437dd8058996e53a728e06205427f712f70bc4c43ab684c53d27d93e59f7a65b00d2b43277cd4e89807324900111af93bca056936bd9f68543a6655efdb89
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2014 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = Reviews
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Reviews'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
@@ -0,0 +1,814 @@
1
+ /*!
2
+ * v0.0.9
3
+ * Copyright (c) 2013 First Opinion
4
+ * formatter.js is open sourced under the MIT license.
5
+ *
6
+ * thanks to digitalBush/jquery.maskedinput for some of the trickier
7
+ * keycode handling
8
+ */
9
+
10
+ ;(function ($, window, document, undefined) {
11
+
12
+ // Defaults
13
+ var defaults = {
14
+ persistent: false,
15
+ repeat: false,
16
+ placeholder: ' '
17
+ };
18
+
19
+ // Regexs for input validation
20
+ var inptRegs = {
21
+ '9': /[0-9]/,
22
+ 'a': /[A-Za-z]/,
23
+ '*': /[A-Za-z0-9]/
24
+ };
25
+
26
+ //
27
+ // Class Constructor - Called with new Formatter(el, opts)
28
+ // Responsible for setting up required instance variables, and
29
+ // attaching the event listener to the element.
30
+ //
31
+ function Formatter(el, opts) {
32
+ // Cache this
33
+ var self = this;
34
+
35
+ // Make sure we have an element. Make accesible to instance
36
+ self.el = el;
37
+ if (!self.el) {
38
+ throw new TypeError('Must provide an existing element');
39
+ }
40
+
41
+ // Merge opts with defaults
42
+ self.opts = utils.extend({}, defaults, opts);
43
+
44
+ // 1 pattern is special case
45
+ if (typeof self.opts.pattern !== 'undefined') {
46
+ self.opts.patterns = self._specFromSinglePattern(self.opts.pattern);
47
+ delete self.opts.pattern;
48
+ }
49
+
50
+ // Make sure we have valid opts
51
+ if (typeof self.opts.patterns === 'undefined') {
52
+ throw new TypeError('Must provide a pattern or array of patterns');
53
+ }
54
+
55
+ self.patternMatcher = patternMatcher(self.opts.patterns);
56
+
57
+ // Upate pattern with initial value
58
+ self._updatePattern();
59
+
60
+ // Init values
61
+ self.hldrs = {};
62
+ self.focus = 0;
63
+
64
+ // Add Listeners
65
+ utils.addListener(self.el, 'keydown', function (evt) {
66
+ self._keyDown(evt);
67
+ });
68
+ utils.addListener(self.el, 'keypress', function (evt) {
69
+ self._keyPress(evt);
70
+ });
71
+ utils.addListener(self.el, 'paste', function (evt) {
72
+ self._paste(evt);
73
+ });
74
+
75
+ // Persistence
76
+ if (self.opts.persistent) {
77
+ // Format on start
78
+ self._processKey('', false);
79
+ self.el.blur();
80
+
81
+ // Add Listeners
82
+ utils.addListener(self.el, 'focus', function (evt) {
83
+ self._focus(evt);
84
+ });
85
+ utils.addListener(self.el, 'click', function (evt) {
86
+ self._focus(evt);
87
+ });
88
+ utils.addListener(self.el, 'touchstart', function (evt) {
89
+ self._focus(evt);
90
+ });
91
+ }
92
+ }
93
+
94
+ //
95
+ // @public
96
+ // Add new char
97
+ //
98
+ Formatter.addInptType = function (chr, reg) {
99
+ inptRegs[chr] = reg;
100
+ };
101
+
102
+ //
103
+ // @public
104
+ // Apply the given pattern to the current input without moving caret.
105
+ //
106
+ Formatter.prototype.resetPattern = function (str) {
107
+ // Update opts to hold new pattern
108
+ this.opts.patterns = str ? this._specFromSinglePattern(str) : this.opts.patterns;
109
+
110
+ // Get current state
111
+ this.sel = inptSel.get(this.el);
112
+ this.val = this.el.value;
113
+
114
+ // Init values
115
+ this.delta = 0;
116
+
117
+ // Remove all formatted chars from val
118
+ this._removeChars();
119
+
120
+ this.patternMatcher = patternMatcher(this.opts.patterns);
121
+
122
+ // Update pattern
123
+ var newPattern = this.patternMatcher.getPattern(this.val);
124
+ this.mLength = newPattern.mLength;
125
+ this.chars = newPattern.chars;
126
+ this.inpts = newPattern.inpts;
127
+
128
+ // Format on start
129
+ this._processKey('', false, true);
130
+ };
131
+
132
+ //
133
+ // @private
134
+ // Determine correct format pattern based on input val
135
+ //
136
+ Formatter.prototype._updatePattern = function () {
137
+ // Determine appropriate pattern
138
+ var newPattern = this.patternMatcher.getPattern(this.val);
139
+
140
+ // Only update the pattern if there is an appropriate pattern for the value.
141
+ // Otherwise, leave the current pattern (and likely delete the latest character.)
142
+ if (newPattern) {
143
+ // Get info about the given pattern
144
+ this.mLength = newPattern.mLength;
145
+ this.chars = newPattern.chars;
146
+ this.inpts = newPattern.inpts;
147
+ }
148
+ };
149
+
150
+ //
151
+ // @private
152
+ // Handler called on all keyDown strokes. All keys trigger
153
+ // this handler. Only process delete keys.
154
+ //
155
+ Formatter.prototype._keyDown = function (evt) {
156
+ // The first thing we need is the character code
157
+ var k = evt.which || evt.keyCode;
158
+
159
+ // If delete key
160
+ if (k && utils.isDelKey(k)) {
161
+ // Process the keyCode and prevent default
162
+ this._processKey(null, k);
163
+ return utils.preventDefault(evt);
164
+ }
165
+ };
166
+
167
+ //
168
+ // @private
169
+ // Handler called on all keyPress strokes. Only processes
170
+ // character keys (as long as no modifier key is in use).
171
+ //
172
+ Formatter.prototype._keyPress = function (evt) {
173
+ // The first thing we need is the character code
174
+ var k, isSpecial;
175
+ // Mozilla will trigger on special keys and assign the the value 0
176
+ // We want to use that 0 rather than the keyCode it assigns.
177
+ if (evt.which) {
178
+ k = evt.which;
179
+ } else {
180
+ k = evt.keyCode;
181
+ isSpecial = utils.isSpecialKey(k);
182
+ }
183
+ // Process the keyCode and prevent default
184
+ if (!utils.isDelKey(k) && !isSpecial && !utils.isModifier(evt)) {
185
+ this._processKey(String.fromCharCode(k), false);
186
+ return utils.preventDefault(evt);
187
+ }
188
+ };
189
+
190
+ //
191
+ // @private
192
+ // Handler called on paste event.
193
+ //
194
+ Formatter.prototype._paste = function (evt) {
195
+ // Process the clipboard paste and prevent default
196
+ this._processKey(utils.getClip(evt), false);
197
+ return utils.preventDefault(evt);
198
+ };
199
+
200
+ //
201
+ // @private
202
+ // Handle called on focus event.
203
+ //
204
+ Formatter.prototype._focus = function () {
205
+ // Wrapped in timeout so that we can grab input selection
206
+ var self = this;
207
+ setTimeout(function () {
208
+ // Grab selection
209
+ var selection = inptSel.get(self.el);
210
+ // Char check
211
+ var isAfterStart = selection.end > self.focus,
212
+ isFirstChar = selection.end === 0;
213
+ // If clicked in front of start, refocus to start
214
+ if (isAfterStart || isFirstChar) {
215
+ inptSel.set(self.el, self.focus);
216
+ }
217
+ }, 0);
218
+ };
219
+
220
+ //
221
+ // @private
222
+ // Using the provided key information, alter el value.
223
+ //
224
+ Formatter.prototype._processKey = function (chars, delKey,ingoreCaret) {
225
+ // Get current state
226
+ this.sel = inptSel.get(this.el);
227
+ this.val = this.el.value;
228
+
229
+ // Init values
230
+ this.delta = 0;
231
+
232
+ // If chars were highlighted, we need to remove them
233
+ if (this.sel.begin !== this.sel.end) {
234
+ this.delta = (-1) * Math.abs(this.sel.begin - this.sel.end);
235
+ this.val = utils.removeChars(this.val, this.sel.begin, this.sel.end);
236
+ }
237
+
238
+ // Delete key (moves opposite direction)
239
+ else if (delKey && delKey == 46) {
240
+ this._delete();
241
+
242
+ // or Backspace and not at start
243
+ } else if (delKey && this.sel.begin - 1 >= 0) {
244
+
245
+ // Always have a delta of at least -1 for the character being deleted.
246
+ this.delta -= 1;
247
+
248
+ // Count number of additional format chars to be deleted. (A group of multiple format chars should be deleted like one value char.)
249
+ while (this.chars[this.focus-1]) {
250
+ this.delta--;
251
+ this.focus--;
252
+ }
253
+
254
+ this.val = utils.removeChars(this.val, this.sel.end + this.delta, this.sel.end);
255
+
256
+ // or Backspace and at start - exit
257
+ } else if (delKey) {
258
+ return true;
259
+ }
260
+
261
+ // If the key is not a del key, it should convert to a str
262
+ if (!delKey) {
263
+ // Add char at position and increment delta
264
+ this.val = utils.addChars(this.val, chars, this.sel.begin);
265
+ this.delta += chars.length;
266
+ }
267
+
268
+ // Format el.value (also handles updating caret position)
269
+ this._formatValue(ingoreCaret);
270
+ };
271
+
272
+ //
273
+ // @private
274
+ // Deletes the character in front of it
275
+ //
276
+ Formatter.prototype._delete = function () {
277
+ // Adjust focus to make sure its not on a formatted char
278
+ while (this.chars[this.sel.begin]) {
279
+ this._nextPos();
280
+ }
281
+
282
+ // As long as we are not at the end
283
+ if (this.sel.begin < this.val.length) {
284
+ // We will simulate a delete by moving the caret to the next char
285
+ // and then deleting
286
+ this._nextPos();
287
+ this.val = utils.removeChars(this.val, this.sel.end -1, this.sel.end);
288
+ this.delta = -1;
289
+ }
290
+ };
291
+
292
+ //
293
+ // @private
294
+ // Quick helper method to move the caret to the next pos
295
+ //
296
+ Formatter.prototype._nextPos = function () {
297
+ this.sel.end ++;
298
+ this.sel.begin ++;
299
+ };
300
+
301
+ //
302
+ // @private
303
+ // Alter element value to display characters matching the provided
304
+ // instance pattern. Also responsible for updating
305
+ //
306
+ Formatter.prototype._formatValue = function (ignoreCaret) {
307
+ // Set caret pos
308
+ this.newPos = this.sel.end + this.delta;
309
+
310
+ // Remove all formatted chars from val
311
+ this._removeChars();
312
+
313
+ // Switch to first matching pattern based on val
314
+ this._updatePattern();
315
+
316
+ // Validate inputs
317
+ this._validateInpts();
318
+
319
+ // Add formatted characters
320
+ this._addChars();
321
+
322
+ // Set value and adhere to maxLength
323
+ this.el.value = this.val.substr(0, this.mLength);
324
+
325
+ // Set new caret position
326
+ if ((typeof ignoreCaret) === 'undefined' || ignoreCaret === false) {
327
+ inptSel.set(this.el, this.newPos);
328
+ }
329
+ };
330
+
331
+ //
332
+ // @private
333
+ // Remove all formatted before and after a specified pos
334
+ //
335
+ Formatter.prototype._removeChars = function () {
336
+ // Delta shouldn't include placeholders
337
+ if (this.sel.end > this.focus) {
338
+ this.delta += this.sel.end - this.focus;
339
+ }
340
+
341
+ // Account for shifts during removal
342
+ var shift = 0;
343
+
344
+ // Loop through all possible char positions
345
+ for (var i = 0; i <= this.mLength; i++) {
346
+ // Get transformed position
347
+ var curChar = this.chars[i],
348
+ curHldr = this.hldrs[i],
349
+ pos = i + shift,
350
+ val;
351
+
352
+ // If after selection we need to account for delta
353
+ pos = (i >= this.sel.begin) ? pos + this.delta : pos;
354
+ val = this.val.charAt(pos);
355
+ // Remove char and account for shift
356
+ if (curChar && curChar == val || curHldr && curHldr == val) {
357
+ this.val = utils.removeChars(this.val, pos, pos + 1);
358
+ shift--;
359
+ }
360
+ }
361
+
362
+ // All hldrs should be removed now
363
+ this.hldrs = {};
364
+
365
+ // Set focus to last character
366
+ this.focus = this.val.length;
367
+ };
368
+
369
+ //
370
+ // @private
371
+ // Make sure all inpts are valid, else remove and update delta
372
+ //
373
+ Formatter.prototype._validateInpts = function () {
374
+ // Loop over each char and validate
375
+ for (var i = 0; i < this.val.length; i++) {
376
+ // Get char inpt type
377
+ var inptType = this.inpts[i];
378
+
379
+ // Checks
380
+ var isBadType = !inptRegs[inptType],
381
+ isInvalid = !isBadType && !inptRegs[inptType].test(this.val.charAt(i)),
382
+ inBounds = this.inpts[i];
383
+
384
+ // Remove if incorrect and inbounds
385
+ if ((isBadType || isInvalid) && inBounds) {
386
+ this.val = utils.removeChars(this.val, i, i + 1);
387
+ this.focusStart--;
388
+ this.newPos--;
389
+ this.delta--;
390
+ i--;
391
+ }
392
+ }
393
+ };
394
+
395
+ //
396
+ // @private
397
+ // Loop over val and add formatted chars as necessary
398
+ //
399
+ Formatter.prototype._addChars = function () {
400
+ if (this.opts.persistent) {
401
+ // Loop over all possible characters
402
+ for (var i = 0; i <= this.mLength; i++) {
403
+ if (!this.val.charAt(i)) {
404
+ // Add placeholder at pos
405
+ this.val = utils.addChars(this.val, this.opts.placeholder, i);
406
+ this.hldrs[i] = this.opts.placeholder;
407
+ }
408
+ this._addChar(i);
409
+ }
410
+
411
+ // Adjust focus to make sure its not on a formatted char
412
+ while (this.chars[this.focus]) {
413
+ this.focus++;
414
+ }
415
+ } else {
416
+ // Avoid caching val.length and this.focus, as they may change in _addChar.
417
+ for (var j = 0; j <= this.val.length; j++) {
418
+ // When moving backwards, i.e. delting characters, don't add format characters past focus point.
419
+ if ( (this.delta <= 0 && j === this.focus && this.chars[j] === undefined) || (this.focus === 0) ) { return true; }
420
+
421
+ // Place character in current position of the formatted string.
422
+ this._addChar(j);
423
+ }
424
+ }
425
+ };
426
+
427
+ //
428
+ // @private
429
+ // Add formattted char at position
430
+ //
431
+ Formatter.prototype._addChar = function (i) {
432
+ // If char exists at position
433
+ var chr = this.chars[i];
434
+ if (!chr) { return true; }
435
+
436
+ // If chars are added in between the old pos and new pos
437
+ // we need to increment pos and delta
438
+ if (utils.isBetween(i, [this.sel.begin -1, this.newPos +1])) {
439
+ this.newPos ++;
440
+ this.delta ++;
441
+ }
442
+
443
+ // If character added before focus, incr
444
+ if (i <= this.focus) {
445
+ this.focus++;
446
+ }
447
+
448
+ // Updateholder
449
+ if (this.hldrs[i]) {
450
+ delete this.hldrs[i];
451
+ this.hldrs[i + 1] = this.opts.placeholder;
452
+ }
453
+
454
+ // Update value
455
+ this.val = utils.addChars(this.val, chr, i);
456
+ };
457
+
458
+ //
459
+ // @private
460
+ // Create a patternSpec for passing into patternMatcher that
461
+ // has exactly one catch all pattern.
462
+ //
463
+ Formatter.prototype._specFromSinglePattern = function (patternStr) {
464
+ return [{ '*': patternStr }];
465
+ };
466
+
467
+
468
+ // Define module
469
+ var pattern = {};
470
+
471
+ // Match information
472
+ var DELIM_SIZE = 4;
473
+
474
+ // Our regex used to parse
475
+ var regexp = new RegExp('{{([^}]+)}}', 'g');
476
+
477
+ //
478
+ // Helper method to parse pattern str
479
+ //
480
+ var getMatches = function (pattern) {
481
+ // Populate array of matches
482
+ var matches = [],
483
+ match;
484
+ while(match = regexp.exec(pattern)) {
485
+ matches.push(match);
486
+ }
487
+
488
+ return matches;
489
+ };
490
+
491
+ //
492
+ // Create an object holding all formatted characters
493
+ // with corresponding positions
494
+ //
495
+ pattern.parse = function (pattern) {
496
+ // Our obj to populate
497
+ var info = { inpts: {}, chars: {} };
498
+
499
+ // Pattern information
500
+ var matches = getMatches(pattern),
501
+ pLength = pattern.length;
502
+
503
+ // Counters
504
+ var mCount = 0,
505
+ iCount = 0,
506
+ i = 0;
507
+
508
+ // Add inpts, move to end of match, and process
509
+ var processMatch = function (val) {
510
+ var valLength = val.length;
511
+ for (var j = 0; j < valLength; j++) {
512
+ info.inpts[iCount] = val.charAt(j);
513
+ iCount++;
514
+ }
515
+ mCount ++;
516
+ i += (val.length + DELIM_SIZE - 1);
517
+ };
518
+
519
+ // Process match or add chars
520
+ for (i; i < pLength; i++) {
521
+ if (mCount < matches.length && i == matches[mCount].index) {
522
+ processMatch(matches[mCount][1]);
523
+ } else {
524
+ info.chars[i - (mCount * DELIM_SIZE)] = pattern.charAt(i);
525
+ }
526
+ }
527
+
528
+ // Set mLength and return
529
+ info.mLength = i - (mCount * DELIM_SIZE);
530
+ return info;
531
+ };
532
+
533
+ //
534
+ // Parse a matcher string into a RegExp. Accepts valid regular
535
+ // expressions and the catchall '*'.
536
+ // @private
537
+ //
538
+ var parseMatcher = function (matcher) {
539
+ if (matcher === '*') {
540
+ return /.*/;
541
+ }
542
+ return new RegExp(matcher);
543
+ };
544
+
545
+ //
546
+ // Parse a pattern spec and return a function that returns a pattern
547
+ // based on user input. The first matching pattern will be chosen.
548
+ // Pattern spec format:
549
+ // Array [
550
+ // Object: { Matcher(RegExp String) : Pattern(Pattern String) },
551
+ // ...
552
+ // ]
553
+ function patternMatcher (patternSpec) {
554
+ var matchers = [],
555
+ patterns = [];
556
+
557
+ // Iterate over each pattern in order.
558
+ utils.forEach(patternSpec, function (patternMatcher) {
559
+ // Process single property object to obtain pattern and matcher.
560
+ utils.forEach(patternMatcher, function (patternStr, matcherStr) {
561
+ var parsedPattern = pattern.parse(patternStr),
562
+ regExpMatcher = parseMatcher(matcherStr);
563
+
564
+ matchers.push(regExpMatcher);
565
+ patterns.push(parsedPattern);
566
+
567
+ // Stop after one iteration.
568
+ return false;
569
+ });
570
+ });
571
+
572
+ var getPattern = function (input) {
573
+ var matchedIndex;
574
+ utils.forEach(matchers, function (matcher, index) {
575
+ if (matcher.test(input)) {
576
+ matchedIndex = index;
577
+ return false;
578
+ }
579
+ });
580
+
581
+ return matchedIndex === undefined ? null : patterns[matchedIndex];
582
+ };
583
+
584
+ return {
585
+ getPattern: getPattern,
586
+ patterns: patterns,
587
+ matchers: matchers
588
+ };
589
+ }
590
+
591
+ // Define module
592
+ var inptSel = {};
593
+
594
+ //
595
+ // Get begin and end positions of selected input. Return 0's
596
+ // if there is no selectiion data
597
+ //
598
+ inptSel.get = function (el) {
599
+ // If normal browser return with result
600
+ if (typeof el.selectionStart == "number") {
601
+ return {
602
+ begin: el.selectionStart,
603
+ end: el.selectionEnd
604
+ };
605
+ }
606
+
607
+ // Uh-Oh. We must be IE. Fun with TextRange!!
608
+ var range = document.selection.createRange();
609
+ // Determine if there is a selection
610
+ if (range && range.parentElement() == el) {
611
+ var inputRange = el.createTextRange(),
612
+ endRange = el.createTextRange(),
613
+ length = el.value.length;
614
+
615
+ // Create a working TextRange for the input selection
616
+ inputRange.moveToBookmark(range.getBookmark());
617
+
618
+ // Move endRange begin pos to end pos (hence endRange)
619
+ endRange.collapse(false);
620
+
621
+ // If we are at the very end of the input, begin and end
622
+ // must both be the length of the el.value
623
+ if (inputRange.compareEndPoints("StartToEnd", endRange) > -1) {
624
+ return { begin: length, end: length };
625
+ }
626
+
627
+ // Note: moveStart usually returns the units moved, which
628
+ // one may think is -length, however, it will stop when it
629
+ // gets to the begin of the range, thus giving us the
630
+ // negative value of the pos.
631
+ return {
632
+ begin: -inputRange.moveStart("character", -length),
633
+ end: -inputRange.moveEnd("character", -length)
634
+ };
635
+ }
636
+
637
+ //Return 0's on no selection data
638
+ return { begin: 0, end: 0 };
639
+ };
640
+
641
+ //
642
+ // Set the caret position at a specified location
643
+ //
644
+ inptSel.set = function (el, pos) {
645
+ // If normal browser
646
+ if (el.setSelectionRange) {
647
+ el.focus();
648
+ el.setSelectionRange(pos,pos);
649
+
650
+ // IE = TextRange fun
651
+ } else if (el.createTextRange) {
652
+ var range = el.createTextRange();
653
+ range.collapse(true);
654
+ range.moveEnd('character', pos);
655
+ range.moveStart('character', pos);
656
+ range.select();
657
+ }
658
+ };
659
+ // Define module
660
+ var utils = {};
661
+
662
+ // Useragent info for keycode handling
663
+ var uAgent = (typeof navigator !== 'undefined') ? navigator.userAgent : null,
664
+ iPhone = /iphone/i.test(uAgent);
665
+
666
+ //
667
+ // Shallow copy properties from n objects to destObj
668
+ //
669
+ utils.extend = function (destObj) {
670
+ for (var i = 1; i < arguments.length; i++) {
671
+ for (var key in arguments[i]) {
672
+ destObj[key] = arguments[i][key];
673
+ }
674
+ }
675
+ return destObj;
676
+ };
677
+
678
+ //
679
+ // Add a given character to a string at a defined pos
680
+ //
681
+ utils.addChars = function (str, chars, pos) {
682
+ return str.substr(0, pos) + chars + str.substr(pos, str.length);
683
+ };
684
+
685
+ //
686
+ // Remove a span of characters
687
+ //
688
+ utils.removeChars = function (str, start, end) {
689
+ return str.substr(0, start) + str.substr(end, str.length);
690
+ };
691
+
692
+ //
693
+ // Return true/false is num false between bounds
694
+ //
695
+ utils.isBetween = function (num, bounds) {
696
+ bounds.sort(function(a,b) { return a-b; });
697
+ return (num > bounds[0] && num < bounds[1]);
698
+ };
699
+
700
+ //
701
+ // Helper method for cross browser event listeners
702
+ //
703
+ utils.addListener = function (el, evt, handler) {
704
+ return (typeof el.addEventListener != "undefined")
705
+ ? el.addEventListener(evt, handler, false)
706
+ : el.attachEvent('on' + evt, handler);
707
+ };
708
+
709
+ //
710
+ // Helper method for cross browser implementation of preventDefault
711
+ //
712
+ utils.preventDefault = function (evt) {
713
+ return (evt.preventDefault) ? evt.preventDefault() : (evt.returnValue = false);
714
+ };
715
+
716
+ //
717
+ // Helper method for cross browser implementation for grabbing
718
+ // clipboard data
719
+ //
720
+ utils.getClip = function (evt) {
721
+ if (evt.clipboardData) { return evt.clipboardData.getData('Text'); }
722
+ if (window.clipboardData) { return window.clipboardData.getData('Text'); }
723
+ };
724
+
725
+ //
726
+ // Returns true/false if k is a del key
727
+ //
728
+ utils.isDelKey = function (k) {
729
+ return k === 8 || k === 46 || (iPhone && k === 127);
730
+ };
731
+
732
+ //
733
+ // Returns true/false if k is an arrow key
734
+ //
735
+ utils.isSpecialKey = function (k) {
736
+ var codes = {
737
+ '9' : 'tab',
738
+ '13': 'enter',
739
+ '35': 'end',
740
+ '36': 'home',
741
+ '37': 'leftarrow',
742
+ '38': 'uparrow',
743
+ '39': 'rightarrow',
744
+ '40': 'downarrow',
745
+ '116': 'F5'
746
+ };
747
+ // If del or special key
748
+ return codes[k];
749
+ };
750
+
751
+ //
752
+ // Returns true/false if modifier key is held down
753
+ //
754
+ utils.isModifier = function (evt) {
755
+ return evt.ctrlKey || evt.altKey || evt.metaKey;
756
+ };
757
+
758
+ //
759
+ // Iterates over each property of object or array.
760
+ //
761
+ utils.forEach = function (collection, callback, thisArg) {
762
+ if (collection.hasOwnProperty("length")) {
763
+ for (var index = 0, len = collection.length; index < len; index++) {
764
+ if (callback.call(thisArg, collection[index], index, collection) === false) {
765
+ break;
766
+ }
767
+ }
768
+ } else {
769
+ for (var key in collection) {
770
+ if (collection.hasOwnProperty(key)) {
771
+ if (callback.call(thisArg, collection[key], key, collection) === false) {
772
+ break;
773
+ }
774
+ }
775
+ }
776
+ }
777
+ };
778
+
779
+ // A really lightweight plugin wrapper around the constructor,
780
+ // preventing against multiple instantiations
781
+ var pluginName = 'formatter';
782
+
783
+ $.fn[pluginName] = function (options) {
784
+
785
+ // Initiate plugin if options passed
786
+ if (typeof options == 'object') {
787
+ this.each(function () {
788
+ if (!$.data(this, 'plugin_' + pluginName)) {
789
+ $.data(this, 'plugin_' + pluginName,
790
+ new Formatter(this, options));
791
+ }
792
+ });
793
+ }
794
+
795
+ // Add resetPattern method to plugin
796
+ this.resetPattern = function (str) {
797
+ this.each(function () {
798
+ var formatted = $.data(this, 'plugin_' + pluginName);
799
+ // resetPattern for instance
800
+ if (formatted) { formatted.resetPattern(str); }
801
+ });
802
+ // Chainable please
803
+ return this;
804
+ };
805
+
806
+ // Chainable please
807
+ return this;
808
+ };
809
+
810
+ $.fn[pluginName].addInptType = function (chr, regexp) {
811
+ Formatter.addInptType(chr, regexp);
812
+ };
813
+
814
+ })( jQuery, window, document);