spina-openinghours 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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);