Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / static / im / js / jquery.dropkick-1.0.0.js @ 0f4a8a68

History | View | Annotate | Download (10.6 kB)

1
/**
2
 * DropKick
3
 *
4
 * Highly customizable <select> lists
5
 * https://github.com/JamieLottering/DropKick
6
 *
7
 * &copy; 2011 Jamie Lottering <http://github.com/JamieLottering>
8
 *                        <http://twitter.com/JamieLottering>
9
 * 
10
 */
11
(function ($, window, document) {
12

    
13
  var ie6 = false;
14

    
15
  // Help prevent flashes of unstyled content
16
  if ($.browser.msie && $.browser.version.substr(0, 1) < 7) {
17
    ie6 = true;
18
  } else {
19
    document.documentElement.className = document.documentElement.className + ' dk_fouc';
20
  }
21
  
22
  var
23
    // Public methods exposed to $.fn.dropkick()
24
    methods = {},
25

    
26
    // Cache every <select> element that gets dropkicked
27
    lists   = [],
28

    
29
    // Convenience keys for keyboard navigation
30
    keyMap = {
31
      'left'  : 37,
32
      'up'    : 38,
33
      'right' : 39,
34
      'down'  : 40,
35
      'enter' : 13
36
    },
37

    
38
    // HTML template for the dropdowns
39
    dropdownTemplate = [
40
      '<div class="dk_container" id="dk_container_{{ id }}" tabindex="{{ tabindex }}">',
41
        '<a class="dk_toggle">',
42
          '<span class="dk_label">{{ label }}</span>',
43
        '</a>',
44
        '<div class="dk_options">',
45
          '<ul class="dk_options_inner">',
46
          '</ul>',
47
        '</div>',
48
      '</div>'
49
    ].join(''),
50

    
51
    // HTML template for dropdown options
52
    optionTemplate = '<li class="{{ current }}"><a data-dk-dropdown-value="{{ value }}">{{ text }}</a></li>',
53

    
54
    // Some nice default values
55
    defaults = {
56
      startSpeed : 1000,  // I recommend a high value here, I feel it makes the changes less noticeable to the user
57
      theme  : false,
58
      change : false
59
    },
60

    
61
    // Make sure we only bind keydown on the document once
62
    keysBound = false
63
  ;
64

    
65
  // Called by using $('foo').dropkick();
66
  methods.init = function (settings) {
67
    settings = $.extend({}, defaults, settings);
68

    
69
    return this.each(function () {
70
      var
71
        // The current <select> element
72
        $select = $(this),
73

    
74
        // Store a reference to the originally selected <option> element
75
        $original = $select.find(':selected').first(),
76

    
77
        // Save all of the <option> elements
78
        $options = $select.find('option'),
79

    
80
        // We store lots of great stuff using jQuery data
81
        data = $select.data('dropkick') || {},
82

    
83
        // This gets applied to the 'dk_container' element
84
        id = $select.attr('id') || $select.attr('name'),
85

    
86
        // This gets updated to be equal to the longest <option> element
87
        width  = settings.width || $select.outerWidth(),
88

    
89
        // Check if we have a tabindex set or not
90
        tabindex  = $select.attr('tabindex') ? $select.attr('tabindex') : '',
91

    
92
        // The completed dk_container element
93
        $dk = false,
94

    
95
        theme
96
      ;
97

    
98
      // Dont do anything if we've already setup dropkick on this element
99
      if (data.id) {
100
        return $select;
101
      } else {
102
        data.settings  = settings;
103
        data.tabindex  = tabindex;
104
        data.id        = id;
105
        data.$original = $original;
106
        data.$select   = $select;
107
        data.value     = _notBlank($select.val()) || _notBlank($original.attr('value'));
108
        data.label     = $original.text();
109
        data.options   = $options;
110
      }
111

    
112
      // Build the dropdown HTML
113
      $dk = _build(dropdownTemplate, data);
114

    
115
      // Make the dropdown fixed width if desired
116
      $dk.find('.dk_toggle').css({
117
        'width' : width + 'px'
118
      });
119

    
120
      // Hide the <select> list and place our new one in front of it
121
      $select.before($dk);
122

    
123
      // Update the reference to $dk
124
      $dk = $('#dk_container_' + id).fadeIn(settings.startSpeed);
125

    
126
      // Save the current theme
127
      theme = settings.theme ? settings.theme : 'default';
128
      $dk.addClass('dk_theme_' + theme);
129
      data.theme = theme;
130

    
131
      // Save the updated $dk reference into our data object
132
      data.$dk = $dk;
133

    
134
      // Save the dropkick data onto the <select> element
135
      $select.data('dropkick', data);
136

    
137
      // Do the same for the dropdown, but add a few helpers
138
      $dk.data('dropkick', data);
139

    
140
      lists[lists.length] = $select;
141

    
142
      // Focus events
143
      $dk.bind('focus.dropkick', function (e) {
144
        $dk.addClass('dk_focus');
145
      }).bind('blur.dropkick', function (e) {
146
        $dk.removeClass('dk_open dk_focus');
147
      });
148

    
149
      setTimeout(function () {
150
        $select.hide();
151
      }, 0);
152
    });
153
  };
154

    
155
  // Allows dynamic theme changes
156
  methods.theme = function (newTheme) {
157
    var
158
      $select   = $(this),
159
      list      = $select.data('dropkick'),
160
      $dk       = list.$dk,
161
      oldtheme  = 'dk_theme_' + list.theme
162
    ;
163

    
164
    $dk.removeClass(oldtheme).addClass('dk_theme_' + newTheme);
165

    
166
    list.theme = newTheme;
167
  };
168

    
169
  // Reset all <selects and dropdowns in our lists array
170
  methods.reset = function () {
171
    for (var i = 0, l = lists.length; i < l; i++) {
172
      var
173
        listData  = lists[i].data('dropkick'),
174
        $dk       = listData.$dk,
175
        $current  = $dk.find('li').first()
176
      ;
177

    
178
      $dk.find('.dk_label').text(listData.label);
179
      $dk.find('.dk_options_inner').animate({ scrollTop: 0 }, 0);
180

    
181
      _setCurrent($current, $dk);
182
      _updateFields($current, $dk, true);
183
    }
184
  };
185

    
186
  // Expose the plugin
187
  $.fn.dropkick = function (method) {
188
    if (!ie6) {
189
      if (methods[method]) {
190
        return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
191
      } else if (typeof method === 'object' || ! method) {
192
        return methods.init.apply(this, arguments);
193
      }
194
    }
195
  };
196

    
197
  // private
198
  function _handleKeyBoardNav(e, $dk) {
199
    var
200
      code     = e.keyCode,
201
      data     = $dk.data('dropkick'),
202
      options  = $dk.find('.dk_options'),
203
      open     = $dk.hasClass('dk_open'),
204
      current  = $dk.find('.dk_option_current'),
205
      first    = options.find('li').first(),
206
      last     = options.find('li').last(),
207
      next,
208
      prev
209
    ;
210

    
211
    switch (code) {
212
      case keyMap.enter:
213
        if (open) {
214
          _updateFields(current.find('a'), $dk);
215
          _closeDropdown($dk);
216
        } else {
217
          _openDropdown($dk);
218
        }
219
        e.preventDefault();
220
      break;
221

    
222
      case keyMap.up:
223
        prev = current.prev('li');
224
        if (open) {
225
          if (prev.length) {
226
            _setCurrent(prev, $dk);
227
          } else {
228
            _setCurrent(last, $dk);
229
          }
230
        } else {
231
          _openDropdown($dk);
232
        }
233
        e.preventDefault();
234
      break;
235

    
236
      case keyMap.down:
237
        if (open) {
238
          next = current.next('li').first();
239
          if (next.length) {
240
            _setCurrent(next, $dk);
241
          } else {
242
            _setCurrent(first, $dk);
243
          }
244
        } else {
245
          _openDropdown($dk);
246
        }
247
        e.preventDefault();
248
      break;
249

    
250
      default:
251
      break;
252
    }
253
  }
254

    
255
  // Update the <select> value, and the dropdown label
256
  function _updateFields(option, $dk, reset) {
257
    var value, label, data;
258

    
259
    value = option.attr('data-dk-dropdown-value');
260
    label = option.text();
261
    data  = $dk.data('dropkick');
262

    
263
    $select = data.$select;
264
    $select.val(value);
265

    
266
    $dk.find('.dk_label').text(label);
267

    
268
    reset = reset || false;
269

    
270
    if (data.settings.change && !reset) {
271
      data.settings.change.call($select, value, label);
272
    }
273
  }
274

    
275
  // Set the currently selected option
276
  function _setCurrent($current, $dk) {
277
    $dk.find('.dk_option_current').removeClass('dk_option_current');
278
    $current.addClass('dk_option_current');
279

    
280
    _setScrollPos($dk, $current);
281
  }
282

    
283
  function _setScrollPos($dk, anchor) {
284
    var height = anchor.prevAll('li').outerHeight() * anchor.prevAll('li').length;
285
    $dk.find('.dk_options_inner').animate({ scrollTop: height + 'px' }, 0);
286
  }
287

    
288
  // Close a dropdown
289
  function _closeDropdown($dk) {
290
    $dk.removeClass('dk_open');
291
  }
292

    
293
  // Open a dropdown
294
  function _openDropdown($dk) {
295
    var data = $dk.data('dropkick');
296
    $dk.find('.dk_options').css({ top : $dk.find('.dk_toggle').outerHeight() - 1 });
297
    $dk.toggleClass('dk_open');
298

    
299
  }
300

    
301
  /**
302
   * Turn the dropdownTemplate into a jQuery object and fill in the variables.
303
   */
304
  function _build (tpl, view) {
305
    var
306
      // Template for the dropdown
307
      template  = tpl,
308
      // Holder of the dropdowns options
309
      options   = [],
310
      $dk
311
    ;
312

    
313
    template = template.replace('{{ id }}', view.id);
314
    template = template.replace('{{ label }}', view.label);
315
    template = template.replace('{{ tabindex }}', view.tabindex);
316

    
317
    if (view.options && view.options.length) {
318
      for (var i = 0, l = view.options.length; i < l; i++) {
319
        var
320
          $option   = $(view.options[i]),
321
          current   = 'dk_option_current',
322
          oTemplate = optionTemplate
323
        ;
324

    
325
        oTemplate = oTemplate.replace('{{ value }}', $option.val());
326
        oTemplate = oTemplate.replace('{{ current }}', (_notBlank($option.val()) === view.value) ? current : '');
327
        oTemplate = oTemplate.replace('{{ text }}', $option.text());
328

    
329
        options[options.length] = oTemplate;
330
      }
331
    }
332

    
333
    $dk = $(template);
334
    $dk.find('.dk_options_inner').html(options.join(''));
335

    
336
    return $dk;
337
  }
338

    
339
  function _notBlank(text) {
340
    return ($.trim(text).length > 0) ? text : false;
341
  }
342

    
343
  $(function () {
344

    
345
    // Handle click events on the dropdown toggler
346
    $('.dk_toggle').live('click', function (e) {
347
      var $dk  = $(this).parents('.dk_container').first();
348

    
349
      _openDropdown($dk);
350

    
351
      if ("ontouchstart" in window) {
352
        $dk.addClass('dk_touch');
353
        $dk.find('.dk_options_inner').addClass('scrollable vertical');
354
      }
355

    
356
      e.preventDefault();
357
      return false;
358
    });
359

    
360
    // Handle click events on individual dropdown options
361
    $('.dk_options a').live(($.browser.msie ? 'mousedown' : 'click'), function (e) {
362
      var
363
        $option = $(this),
364
        $dk     = $option.parents('.dk_container').first(),
365
        data    = $dk.data('dropkick')
366
      ;
367
    
368
      _closeDropdown($dk);
369
      _updateFields($option, $dk);
370
      _setCurrent($option.parent(), $dk);
371
    
372
      e.preventDefault();
373
      return false;
374
    });
375

    
376
    // Setup keyboard nav
377
    $(document).bind('keydown.dk_nav', function (e) {
378
      var
379
        // Look for an open dropdown...
380
        $open    = $('.dk_container.dk_open'),
381

    
382
        // Look for a focused dropdown
383
        $focused = $('.dk_container.dk_focus'),
384

    
385
        // Will be either $open, $focused, or null
386
        $dk = null
387
      ;
388

    
389
      // If we have an open dropdown, key events should get sent to that one
390
      if ($open.length) {
391
        $dk = $open;
392
      } else if ($focused.length && !$open.length) {
393
        // But if we have no open dropdowns, use the focused dropdown instead
394
        $dk = $focused;
395
      }
396

    
397
      if ($dk) {
398
        _handleKeyBoardNav(e, $dk);
399
      }
400
    });
401
  });
402
})(jQuery, window, document);